package SSF.OS.OSPFv2.test;

/**
 * TOSDump.java (modified DumpPro.java)
 *
 * @version 0.2.1
 */

import java.io.*;
import java.util.Calendar;

import SSF.OS.OSPFv2.*;

/** A standalone class that translates binaryTcpDump files into ASCII
  *  format. This is a modified DumpPro, which dumps OSPF packets. Parts of the code
  * were taken from SSF.OS.DumpPro.
  */
public class TOSDump  {

  /********************* Attribute Variables *********************/

class DumpFileHeader {
  int magic;   //magic number for tcpdump files
  int major;   //major version number
  int minor;   //minor version number
  int zone;    // gmt to local correction
  int sigfigs; //accuracy of timestamp
  int snaplen; //max length saved portion of each packet
  int linktype;//datalink type
}
class PktHeader {
  int second;
  int microsec;
  int portion;
  int pktlen;
}

  class IPHeader{
    byte ip_ver;       // ip version number
    byte type_ser;     // type of service
    short byte_count;  //byte count of ip packet
    short data_id;     //datagram id
    short flag_offset; //flag and fragment offset
    byte ttl;          //time to live
    byte pro_num;      //ip protocol number
    short ip_checksum; //ip header check sum
    int src_ip;        // source ip address
    int dest_ip;       //destination ip address
  }

  static final int DLT_NULL = 0;       /* no link-layer encapsulation */
  static final int DLT_EN10MB = 1;       /* Ethernet (10Mb) */
  static final int DLT_SLIP = 8;       /* Serial Line IP */
  static final int DLT_FDDI = 10;      /* FDDI */
  static final int DLT_RAW = 12;      /* raw IP */

  static final int TIMESTAMP = 30;
  static final int PKT_LEN = 29;
  static final int SRC_IP = 28;
  static final int DEST_IP = 27;
  static final int BYTE_COUNT = 26;
  static final int PROTOCOL = 25;
  static final int TTL = 24;
  static final int SRC_PORT = 23;
  static final int DEST_PORT = 22;
  static final int SEQ_NO = 21;
  static final int ACK_NO = 20;
  static final int FLAGS = 19;
  static final int ADV_WIN = 18;
  static final int OFFSET = 17;
  static final int DATLEN = 16;
  static final int SYN = 15;
  static final int FIN = 14;
  static final int ACK = 13;
  

  DataInputStream in;
  boolean swap = false;
  int link_hdr_len = 0;
  /************************* Constructors ************************/

  public TOSDump(String infile, String option) throws IOException{
    in = new DataInputStream(new BufferedInputStream
      (new FileInputStream(infile)));

    format();
    
  }
  
  int[] compact(int[] ar){
    int last = 0;
    for(int i = 0; i < ar.length; i++)
      if(ar[i] != 0)
	ar[last++] = ar[i];
    int[] com_ar = new int[last];
    for(int i = 0; i < last; i++) com_ar[i] = ar[i];
    return com_ar;
  }

  /************************ Class Methods ***********************/

  /** read information from binary tcpdump file header */
  public DumpFileHeader getFileHeader() throws IOException{
    byte[] b4 = new byte[4];
    DumpFileHeader hdr = new DumpFileHeader();
    int n = 0;
    if((n = in.read(b4)) != -1){
      hdr.magic = byteToInt(swap, b4);
      if(hdr.magic != 0xA1B2C3D4){
	if(hdr.magic != 0xD4C3B2A1){
	  System.err.println("Wrong file type");
	  System.exit(0);
	}else {
	  swap = !swap;
	}  
      } 
    }
    if((n = in.read(b4)) != -1){
      n = byteToInt(swap, b4);
      hdr.major = n >> 16;
      hdr.minor = n - (hdr.major << 16);
    }
    if((n = in.read(b4)) != -1){
      hdr.zone = byteToInt(swap, b4);
    } 
    if((n = in.read(b4)) != -1){
      hdr.sigfigs =byteToInt(swap, b4);
    } 
    if((n = in.read(b4)) != -1){
      hdr.snaplen = byteToInt(swap, b4);
    } 
    if((n = in.read(b4)) != -1){
      hdr.linktype = byteToInt(swap, b4);
    } 
    return hdr;
  }
  /** read packet tcpdump header */
  public PktHeader getPktHeader() throws IOException{
    byte b4[] = new byte[4];
    int n = 0;

    PktHeader hdr = new PktHeader();/* 20 bytes 5 fields*/
    if((n = in.read(b4)) != -1){
      hdr.second = byteToInt(swap, b4); 
    }
    if((n = in.read(b4)) != -1){
      hdr.microsec = byteToInt(swap, b4); 
    }
    if((n = in.read(b4)) != -1){
      hdr.portion = byteToInt(swap, b4);
    } 
    if((n = in.read(b4)) != -1){
      hdr.pktlen = byteToInt(swap, b4);
    }
    if(n == -1) return null;
    return hdr;
  }
  int init_second;
  int init_microsec;
  public double getTime(PktHeader hdr)throws IOException{
    double sec = (double)(hdr.second - init_second);
    double msec = (double)(hdr.microsec - init_microsec);
    return sec + msec/(double)1000000;
  }
  /** read ip header */
  public IPHeader getIpHeader()throws IOException{

    byte b2[] = new byte[2];
    byte b4[] = new byte[4];
    int n = 0;
    IPHeader hdr = new IPHeader();/* 20 bytes 10 fields*/
    hdr.ip_ver = in.readByte();
    hdr.type_ser = in.readByte(); 
    if((n = in.read(b2)) != -1){
      hdr.byte_count = (short)byteToInt(false, b2); 
    }
    if((n = in.read(b2)) != -1){
      hdr.data_id = (short)byteToInt(false, b2); 
    }
    if((n = in.read(b2)) != -1){
      hdr.flag_offset = (short)byteToInt(false, b2); 
    }
    hdr.ttl = in.readByte();
    hdr.pro_num =  in.readByte();
    if((n = in.read(b2)) != -1){
      hdr.ip_checksum = (short)byteToInt(false, b2); 
    }
    if((n = in.read(b4)) != -1){
      hdr.src_ip = byteToInt(false, b4); 
    }
    if((n = in.read(b4)) != -1){
      hdr.dest_ip = byteToInt(false, b4); 
    }
    if(n == -1)return null;
    return hdr;
  }
  public int[] scanForIpAddr() throws IOException{
    byte b4[] = new byte[4];
    int ipAddr[] = new int[2];
    
    in.skipBytes(12);
    int n = in.read(b4);
    if(n != -1){
      ipAddr[0] = byteToInt(false, b4);
    }
    if((n = in.read(b4)) != -1){
      ipAddr[1] = byteToInt(false, b4);
    }
    if(n == -1)return null;
    return ipAddr;
  }
  
  /* filter variable */
  static double stime = 0;   //print time series between stime and etime
  static double etime = Double.MAX_VALUE; 
  static int sip = -1;//print when source IP and destination IP are matched
  static int dip = -1; // -1 is wildcast   
  static int sport = -1; // -1 is wildcast   
  static int dport = -1; // -1 is wildcast   
                      
  static byte flag_mask = (byte)0;  //   Mask bit to find SYN, FIN, ACK
  static boolean data = false;    // if true print packet only contains data 

  /** output with standard format */
  public void format(){
    try {
      DumpFileHeader hdr = getFileHeader();
      switch (hdr.linktype) {
      case DLT_NULL:
	link_hdr_len = 4;
	break;
      case DLT_EN10MB:
	link_hdr_len = 14;
	break; 
      case DLT_SLIP:
	link_hdr_len = 16;
	break; 
      case DLT_FDDI:
	link_hdr_len = 13+8;
	break; 
      case DLT_RAW:
	link_hdr_len = 0;
	break; 
      default:
	System.out.println("Unknown link type value: " + hdr.linktype);
	System.exit(0);
	break;
      }   // end of switch (linktype)

      PrintWriter out = new PrintWriter(new BufferedWriter
	(new OutputStreamWriter(System.out)));

      PktHeader pkHeader;
      StringBuffer lineBuffer = new StringBuffer(200);
      while((pkHeader = getPktHeader()) != null){
	double time = getTime(pkHeader);
	int cap_len = (pkHeader.pktlen<pkHeader.portion)?
					pkHeader.pktlen:pkHeader.portion;

	if(time < stime) {
	  in.skipBytes(cap_len);
	  continue;
	}
        if(time > etime) break; //stop scan, suppose timestamp is in order
	in.skipBytes(link_hdr_len);
	IPHeader ipHdr = getIpHeader();
	in.skipBytes(ipHdr.byte_count - 20);
	
	if((sip != -1)&&(sip != ipHdr.src_ip)) break;
      	if((dip != -1)&&(dip != ipHdr.dest_ip)) break;
      		
      	// the standard output: timestamp, src and dest address
        long second = pkHeader.second & 0xffffffffL;
      	long min = second/60;
      	long hour = (min/60)%24;
      	float sec = second - 60*min + pkHeader.microsec/(float)1000000;
      	min = min%60;
      	lineBuffer.append(hour<10?"0":"").append(hour).append(":")
		.append(min<10?"0":"").append(min).append(":")
		.append(sec<10?"0":"").append(sec).append(" ");
      	lineBuffer.append(IPtoString(ipHdr.src_ip))
		.append(" > ").append(IPtoString(ipHdr.dest_ip));
		
	lineBuffer.append(" : IPv").append((ipHdr.ip_ver-5)>>4).append(" tos ")
		.append(ipHdr.type_ser).append(" ttl ")
		.append(ipHdr.ttl).append(" proto ")
		.append(ipHdr.pro_num);
		
	/* todo: print flags */
	
	lineBuffer.append("\n");
	out.print(lineBuffer);
      	lineBuffer.setLength(0);
      }

      in.close();
      out.close();
    } catch (IOException e) {
      System.err.println(e);
    } 
  }
  

  // ported from SSF.Net.Util 
  public static String IPtoString (int ipAddr){
    int a = (ipAddr >> 24) & 0xFF;
    int b = (ipAddr >> 16) & 0xFF;
    int c = (ipAddr >>  8) & 0xFF;
    int d = (ipAddr      ) & 0xFF;
    return (a+"."+b+"."+c+"."+d);
  }

  public int byteToInt(boolean swap, byte[] b){
    int len = b.length;
    if((len < 1) || (len > 4)) {
      System.err.println("Usage error: only 1~4 bytes integer supported");
      System.exit(1);
    }
    int n = 0;
    if (swap) {
      switch (len) {
      case 4:
	n |= (b[3]&0xff) << 24;
      case 3:
	n |= (b[2]&0xff) << 16;
      case 2:
	n |= (b[1]&0xff) << 8;
      case 1:
	n |= b[0]&0xff;
      }
    } else {
      switch (len) {
      case 4:
	n  = (b[0]&0xff) << 24;
	n |= (b[1]&0xff) << 16;
	n |= (b[2]&0xff) << 8;
	n |= b[3]&0xff;
	break;
      case 3:
	n  = (b[0]&0xff) << 16;
	n |= (b[1]&0xff) << 8;
	n |= b[2]&0xff;
	break;
      case 2:
	n  = (b[0]&0xff) << 8;
	n |= b[1]&0xff;
	break;
      case 1:
	n  = b[0]&0xff;
	break;
      }
    }
    return n;
  }
  public static void main (String[] args) {
    //System.err.println("1: " + Calendar.getInstance().getTime());
    String filename = args[0];
    String option = "";
    int i = 0;
    while(++i < args.length){
      if(args[i].startsWith("-")){
        if(args[i].compareTo("-t") == 0){
  	  stime = (new Float(args[++i])).floatValue();
	  etime = (new Float(args[++i])).floatValue();
        }else if(args[i].compareTo("-ip") == 0){
	  sip = (new Integer(args[++i])).intValue();
	  dip = (new Integer(args[++i])).intValue();
        }else if(args[i].compareTo("-port") == 0){
          sport = (new Integer(args[++i])).intValue();
          dport = (new Integer(args[++i])).intValue();
        }else if(args[i].compareTo("-f") == 0){
	  if(args[++i].indexOf("s") != -1)flag_mask |= 2;
	  if(args[i].indexOf("f") != -1)flag_mask |= 1;
	  if(args[i].indexOf("a") != -1)flag_mask |= 16;
        } else if(args[i].compareTo("-d") == 0){
	  data = true;
        }
      }else
	option = option + args[i];
    }

    try {
      //System.err.println("2: " + Calendar.getInstance().getTime());
      TOSDump dump = new TOSDump(filename,option);
    } catch (IOException e) {
      System.out.println(e);
    } 

  } // end of main()
} // TOSDump
